from multiprocessing import Lock
from heapq import heappush, heappop, nsmallest

_locks = {}
_locks_lock = Lock( )

def get_lock( item ):
	"""
	If necessary create and acquire a lock on an object.
	"""
	_locks_lock.acquire( )
    
	if id( item ) in _locks:
		_locks[id( item )].acquire( )
	else:
		_locks[id( item )] = Lock( )
		_locks[id( item )].acquire( )
    
	_locks_lock.release( )
    
def release_lock( item ):
	"""
	Release a lock on an object.
	"""
	_locks[id( item )].release( )

def synchronized( fun ):
	"""Decorator to make a function object thread-safe."""
	def decorated( *args, **kwargs ):
		get_lock( fun )
		fun( *args, **kwargs )
		release_lock( fun )
	return decorated

def synchronized_on( obj : "The object that this function should lock against." ):
	"""
	Decorator to make a function object thread-safe on a given object.
	"""
	def decorated( fun ):
		def decorator( *args, **kwargs ):
			get_lock( obj )
			fun( *args, **kwargs )
			release_lock( obj )
		return decorator
	return decorated

def synchronized_on_member( name : "Name of the member to lock" ):
	"""
	Decorator to make a class method synchronize on a member of class.
	"""
	def decorated( fun ):
		def decorator( *args, **kwargs ):
			obj = getattr( args[0], name )
			get_lock( obj )
			fun( *args, **kwargs )
			release_lock( obj )
		return decorator
	return decorated

def synchronized_on_expression( expr : "Expression to evaluate." ):
	"""
	Decorator to make a class method synchronize on the result of an expression.
	"""
	def decorated( fun ):
		def decorator( *args, **kwargs ):
			obj = eval( expr )
			get_lock( obj )
			fun( *args, **kwargs )
			release_lock( obj )
		return decorator
	return decorated

class Priority_Queue:
	"""A non-threadsafe priority queue implementation."""
	__slots__ = ['queue','size']
	
	def __init__( self ):
		"""Create a priority queue."""
		self.queue = []
		self.size = 0

	def push( self, item ):
		"""Push an item onto the queue."""
		heappush( self.queue, item )
		self.size += 1

	def pop( self ):
		"""Pop an item off the queue."""
		if self.size > 0:
			self.size -= 1
			return heappop( self.queue )
		else:
			return None

	def peek( self ):
		"""Peek at the first item on the queue."""
		if self.size > 0:
			return nsmallest( 1, self.queue )[0]
		else:
			return None
